package cn.edu.usst.cs.chat_room01;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

@WebServlet(name = "helloServlet", value = "/hello")
public class HelloServlet extends HttpServlet implements ServletInterface {

    public static String PROJ = "chat_room01";


    private static HelloServlet instance;

    public static HelloServlet getInstance() {
        return instance;
    }

    /**
     * {@code key}用户名
     * {@code value}验证码/LOGGED_IN
     */
    private Map<String, String> userStatus;
    public static final int CAPTCHA_LENGTH = 6;

    public void init() {
        userStatus = new HashMap<>();
        if (instance == null) {
            instance = this;
        }
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        response.setContentType("text/html");

        String user = request.getHeader(USER);
        DISPATCH_HEADER:
        {
            if (user != null) {
                String chat_receiver = request.getHeader(CHATROOM_SELECT_RECEIVER);
                if ("__all__".equals(user)) {
                    back(request, response, "该名称为保留字符");
                    return;
                }
                if (chat_receiver == null) break DISPATCH_HEADER;
                if (!"true".equals(chat_receiver)) break DISPATCH_HEADER;
                //if the code reaches here, the request is from chatroom.jsp
                // asking for all existing users
                wrapMessage(response, loggedUsers(user));
                return;
            }
            //user is null
            String referer = request.getHeader("referer");
            System.out.println(referer);
            back(request, response, "无法解析用户名");
            return;
        }
        //the user is got, yet receiver_select is not flagged
        //this means this user is new to this site
        request.getSession().setAttribute(USER, user);
        request.getSession().setMaxInactiveInterval(5 * 60);
        if (userStatus.containsKey(user)) {
            String status = userStatus.get(user);
            try {
                Integer.parseInt(status);
                userStatus.remove(user);
            } catch (NumberFormatException e) {
                back(request, response, user + "：用户名已注册");
                return;
            }
            //用户重试captcha
        }
        //尝试注册用户
        if (packCAPTCHAResult(response, user)) {
            back(request, response, "获取验证码失败");
        }
    }

    private boolean packCAPTCHAResult(HttpServletResponse response, String user) throws IOException {
        char[] captcha = generateCAPTCHA(user);
        if (captcha == null) return true;
        JSONObject obj = new JSONObject();
        for (int i = 0; i < CAPTCHA_LENGTH; i++) {
            char index = (char) ('0' + i);
            char captcha_code = captcha[i];
            obj.put(captchaATTR(index), captchaPath(captcha_code));
        }
        userStatus.put(user, new String(captcha));
        try (Writer writer = response.getWriter()) {
            writer.write(obj.toString());
        }
        return false;
    }

    public static void back(HttpServletRequest request, HttpServletResponse response, String message) throws IOException, ServletException {
        request.getSession().setAttribute(WARNING, message);
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }

    public synchronized static String requestReadout(HttpServletRequest request) throws IOException {
//        StringBuilder buffer = new StringBuilder();
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            String ret_str = new String(inputStream.readAllBytes());
            return ret_str;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    @NotNull
    public static Map<String, String> parseFormSubmitMessage(@NotNull String message) {
        Map<String, String> map = new HashMap<>();
        if (message.contains("{") && message.contains("}")) {
            JSONObject obj = parseJSONBuf(message);
            if (obj == null) {
                return map;
            }
            for (String key : obj.keySet()) {
                map.put(key, obj.getString(key));
            }
            return map;
        }
        for (String pair : message.split("&")) {
            String[] keyValue = pair.split("=");
            if (keyValue.length != 2) continue;
            map.put(keyValue[0], keyValue[1]);
        }
        return map;
    }

    @Nullable
    public static JSONObject parseJSONBuf(@NotNull String message) {
        try {
            return new JSONObject(message);
        } catch (JSONException e) {
            return null;
        }
    }

    public static void wrapMessage(@NotNull HttpServletResponse response, String message) throws IOException {
        JSONObject obj = new JSONObject();
        obj.put("warning", message);
        response.setContentType("application/json");
        try (PrintWriter out = response.getWriter()) {
            out.write(obj.toString());
        }
    }

    public static void wrapMessage(@NotNull HttpServletResponse response, JSONArray message) throws IOException {
        response.setContentType("application/json");
        try (PrintWriter out = response.getWriter()) {
            out.write(message.toString());
        }
    }


    @NotNull
    @Contract(pure = true)
    public static String captchaATTR(char digit) {
        return String.format("PIC_%c", digit);
    }

    @NotNull
    @Contract(pure = true)
    private static String captchaPath(char digit) {
        return String.format("captcha_images/%c.png", digit);
    }

    /**
     * validate whether this is a user who needs a captcha
     *
     * @param user
     * @return {@code null} if not necessary
     * else {@code char[6]}
     */

    private char[] generateCAPTCHA(String user) {
        try {
            Integer.parseInt(userStatus.get(user));
            //if this succeeds, the user is already using a captcha
            return null;
        } catch (Exception ignored) {
        }
        char[] captcha = new char[CAPTCHA_LENGTH];
        for (int i = 0; i < CAPTCHA_LENGTH; i++) {
            char show_num = (char) ('0' + (int) (Math.random() * 10));
            captcha[i] = show_num;
        }
        return captcha;
    }

    private JSONArray loggedUsers(String self) {
        JSONArray array = new JSONArray();
        for (String user : userStatus.keySet()) {
            if (user.equals(self)) {
                continue;
            }
            try {
                String status = userStatus.get(user);
                Integer.parseInt(status);
            } catch (Exception exception) {
                array.put(user);
            }
        }
        return array;
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        var map = parseFormSubmitMessage(requestReadout(request));
        String user, captcha, key;
        try {
            user = map.get(USERNAME_READONLY);
            captcha = map.get(CAPTCHA_ATTR);
        } catch (NullPointerException exception) {
            back(request, response, "缺失信息 " + exception.getMessage());
            return;
        }
        key = userStatus.get(user);
        try {
            Integer.parseInt(key);
            if (!key.equals(captcha)) {

                back(request, response, "验证码无效");
                return;
            }
        } catch (NumberFormatException exception) {
            request.getSession().setAttribute("warning", "repeat");
        } catch (NullPointerException exception) {
            //尚无该用户
            back(request, response, "请先注册");
            return;
        }
        userStatus.put(user, LOGGED_IN);
        request.getSession().setAttribute(USER, user);
        request.getRequestDispatcher("chatroom.jsp").forward(request, response);
    }

    public void logout(String user) {
        userStatus.remove(user);
    }

    public void logout(UserActivityListener listener) {

    }
}